
import tushare as ts
import pandas as pd

# 初始化接口
pro = ts.pro_api('d3bdfac8ffb0ad7ae6c830bed29e28580b51f91ee085a6866c238d84')

# 设置参数
stock_list = ['600519.SH', '000858.SZ', '601318.SH', '002594.SZ', '300750.SZ']
start_date = '20230101'
end_date = '20231231'

# 拉取并保存数据
all_data = []

for code in stock_list:
    print(f"正在获取：{code}")
    df = pro.daily(ts_code=code, start_date=start_date, end_date=end_date)
    df['stock'] = code
    all_data.append(df)

# 合并数据
final_df = pd.concat(all_data)
final_df = final_df.sort_values(['stock', 'trade_date'])

# 保存为 CSV 文件
final_df.to_csv('C:/Users/Administrator/Desktop/five_stocks_kline_2023.csv', index=False)
print("数据已保存为 five_stocks_kline_2023.csv")

import pandas as pd

# 读入数据
df = pd.read_csv('five_stocks_kline_2023.csv')

# 查看数据结构，确认无误
print(df.head())

# 选取一只股票，比如贵州茅台（600519.SH）
stock_code = '600519.SH'
df_stock = df[df['ts_code'] == stock_code].copy()

# 按交易日期排序，保证时间顺序
df_stock = df_stock.sort_values(by='trade_date')

# 把日期列转成时间格式（方便后续分析）
df_stock['trade_date'] = pd.to_datetime(df_stock['trade_date'])

# 只保留策略需要的列
df_stock = df_stock[['trade_date', 'open', 'high', 'low', 'close', 'vol']]


# 复用之前的策略A函数
def ma_strategy(df):
    df = df.copy()
    df['MA5'] = df['close'].rolling(window=5).mean()
    df['MA20'] = df['close'].rolling(window=20).mean()
    df.dropna(inplace=True)

    df['signal'] = 0
    df.loc[df['MA5'] > df['MA20'], 'signal'] = 1
    df.loc[df['MA5'] < df['MA20'], 'signal'] = -1

    cash = 100000
    hold = 0
    buy_price = 0
    portfolio_value = []

    for idx, row in df.iterrows():
        price = row['close']
        sig = row['signal']

        if hold > 0:
            ret = (price - buy_price) / buy_price
            if ret >= 0.10 or ret <= -0.05 or sig == -1:
                cash += hold * price
                hold = 0
                buy_price = 0

        if sig == 1 and hold == 0:
            hold = cash // price
            cash -= hold * price
            buy_price = price

        total_value = cash + hold * price
        portfolio_value.append(total_value)

    df['portfolio_value'] = portfolio_value
    df['returns'] = df['portfolio_value'].pct_change().fillna(0)
    total_return = (portfolio_value[-1] - 100000) / 100000
    print(f"策略A 总收益率: {total_return:.2%}")

    return df


# 执行回测
result_df = ma_strategy(df_stock)

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置中文字体为黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
import matplotlib.pyplot as plt

plt.figure(figsize=(12,6))
plt.plot(result_df['trade_date'], result_df['portfolio_value'], label='组合净值')
plt.title('均线交叉策略净值曲线')
plt.xlabel('日期')
plt.ylabel('资产净值')
plt.legend()
plt.show()

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# 读入合并后的五只股票数据
df = pd.read_csv('five_stocks_kline_2023.csv', parse_dates=['trade_date'])
df.sort_values(['ts_code', 'trade_date'], inplace=True)

# 只保留执行的列
df = df[['ts_code', 'trade_date', 'open', 'high', 'low', 'close', 'vol']]

def prepare_ml_data(df_stock):
    df_stock = df_stock.copy()
    df_stock['ret_1'] = df_stock['close'].pct_change(1)
    df_stock['ret_2'] = df_stock['close'].pct_change(2)
    df_stock['vol_ma5'] = df_stock['vol'].rolling(5).mean()
    df_stock['vol_ratio'] = df_stock['vol'] / df_stock['vol_ma5']
    df_stock['MA5'] = df_stock['close'].rolling(5).mean()
    df_stock['MA20'] = df_stock['close'].rolling(20).mean()
    df_stock['ma5_diff'] = (df_stock['MA5'] - df_stock['MA20']) / df_stock['MA20']

    # 构造标签：次日涨跌
    df_stock['next_close'] = df_stock['close'].shift(-1)
    df_stock['y'] = (df_stock['next_close'] > df_stock['close']).astype(int)

    df_stock.dropna(inplace=True)

    features = ['ret_1', 'ret_2', 'vol_ratio', 'ma5_diff']
    X = df_stock[features]
    y = df_stock['y']
    dates = df_stock['trade_date']

    return X, y, dates, df_stock

# 筛选贵州茅台
df_mgt = df[df['ts_code']=='600519.SH'].copy()

# 准备特征和标签
X, y, dates, df_feat = prepare_ml_data(df_mgt)

# 划分训练/测试集（不打乱顺序）
X_train, X_test, y_train, y_test, dates_train, dates_test = train_test_split(
X, y, dates, test_size=0.2, shuffle=False)

# 训练随机森林
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# 预测测试集
y_pred = model.predict(X_test)

# 将预测信号合并回 df_feat
df_test = df_feat.loc[dates_test.index].copy()
df_test['pred'] = y_pred

def ml_backtest(df_test, initial_cash=100000):
    cash = initial_cash
    hold = 0
    buy_price = 0
    portfolio = []

    for idx, row in df_test.iterrows():
        price = row['close']
        sig = row['pred']

        # 卖出：预测跌或止盈/止损
        if hold > 0:
            ret = (price - buy_price) / buy_price
            if sig == 0 or ret >= 0.10 or ret <= -0.05:
                cash += hold * price
                hold = 0
                buy_price = 0

        # 买入：预测涨且无持仓
        if sig == 1 and hold == 0:
            hold = cash // price
            cash -= hold * price
            buy_price = price

        portfolio.append(cash + hold * price)

    total_return = (portfolio[-1] - initial_cash) / initial_cash
    print(f"策略B 总收益率: {total_return:.2%}")
    return portfolio

# 执行回测
portfolio_values = ml_backtest(df_test)

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

# 读取合并后的 CSV（本地运行）
df = pd.read_csv('five_stocks_kline_2023.csv', parse_dates=['trade_date'])
df.sort_values(['ts_code', 'trade_date'], inplace=True)

# 策略A：均线交叉
def ma_strategy_return(df_stock, initial_cash=100000):
    df = df_stock.copy()
    df['MA5'] = df['close'].rolling(5).mean()
    df['MA20'] = df['close'].rolling(20).mean()
    df.dropna(inplace=True)
    df['signal'] = 0
    df.loc[df['MA5'] > df['MA20'], 'signal'] = 1
    df.loc[df['MA5'] < df['MA20'], 'signal'] = -1

    cash, hold, buy_price = initial_cash, 0, 0
    for _, row in df.iterrows():
        price, sig = row['close'], row['signal']
        # 卖出
        if hold > 0:
            ret = (price - buy_price) / buy_price
            if sig == -1 or ret >= 0.10 or ret <= -0.05:
                cash += hold * price
                hold, buy_price = 0, 0
        # 买入
        if sig == 1 and hold == 0:
            hold = cash // price
            cash -= hold * price
            buy_price = price
    return (cash + hold * df.iloc[-1]['close'] - initial_cash) / initial_cash * 100

# 策略B：随机森林涨跌预测
def prepare_ml_data(df_stock):
    df = df_stock.copy()
    df['ret_1'] = df['close'].pct_change(1)
    df['ret_2'] = df['close'].pct_change(2)
    df['vol_ma5'] = df['vol'].rolling(5).mean()
    df['vol_ratio'] = df['vol'] / df['vol_ma5']
    df['MA5'] = df['close'].rolling(5).mean()
    df['MA20'] = df['close'].rolling(20).mean()
    df['ma5_diff'] = (df['MA5'] - df['MA20']) / df['MA20']
    df['next_close'] = df['close'].shift(-1)
    df['y'] = (df['next_close'] > df['close']).astype(int)
    df.dropna(inplace=True)
    X = df[['ret_1', 'ret_2', 'vol_ratio', 'ma5_diff']]
    y = df['y']
    return X, y, df

def ml_strategy_return(df_stock, initial_cash=100000):
    X, y, df_feat = prepare_ml_data(df_stock)
    split = int(len(X) * 0.8)
    X_train, X_test = X.iloc[:split], X.iloc[split:]
...     y_train = y.iloc[:split]
...     df_test = df_feat.iloc[split:].copy()
...     model = RandomForestClassifier(n_estimators=100, random_state=42)
...     model.fit(X_train, y_train)
...     df_test['pred'] = model.predict(X_test)
... 
...     cash, hold, buy_price = initial_cash, 0, 0
...     for _, row in df_test.iterrows():
...         price, sig = row['close'], row['pred']
...         # 卖出
...         if hold > 0:
...             ret = (price - buy_price) / buy_price
...             if sig == 0 or ret >= 0.10 or ret <= -0.05:
...                 cash += hold * price
...                 hold, buy_price = 0, 0
...         # 买入
...         if sig == 1 and hold == 0:
...             hold = cash // price
...             cash -= hold * price
...             buy_price = price
...     return (cash + hold * df_test.iloc[-1]['close'] - initial_cash) / initial_cash * 100
... 
... # 批量跑所有股票
... results = []
... for code in df['ts_code'].unique():
...     df_s = df[df['ts_code']==code].copy()
...     df_s.sort_values('trade_date', inplace=True)
...     a_ret = ma_strategy_return(df_s)
...     b_ret = ml_strategy_return(df_s)
...     results.append({
...         'ts_code': code,
...         'MA_Crossover (%)': round(a_ret, 2),
...         'RF_Prediction (%)': round(b_ret, 2)
...     })
... 
... # 输出对比表
... results_df = pd.DataFrame(results)
... print(results_df)
... 


import matplotlib.pyplot as plt
import numpy as np

# 设置图形大小和样式
plt.figure(figsize=(10, 6))
bar_width = 0.35
index = np.arange(len(results_df))

# 绘图
plt.bar(index, results_df['MA_Crossover (%)'], bar_width, label='策略A - MA交叉', color='skyblue')
plt.bar(index + bar_width, results_df['RF_Prediction (%)'], bar_width, label='策略B - 随机森林', color='orange')

# 添加细节
plt.xlabel('股票代码')
plt.ylabel('收益率 (%)')
plt.title('策略收益率对比柱状图')
plt.xticks(index + bar_width / 2, results_df['ts_code'], rotation=45)
plt.axhline(0, color='gray', linestyle='--', linewidth=0.8)
plt.legend()
plt.tight_layout()
plt.grid(axis='y', linestyle='--', alpha=0.6)

# 展示图像
plt.show()

plt.figure(figsize=(10, 6))

plt.plot(results_df['ts_code'], results_df['MA_Crossover (%)'], marker='o', label='策略A - MA交叉')
plt.plot(results_df['ts_code'], results_df['RF_Prediction (%)'], marker='s', label='策略B - 随机森林')

plt.xlabel('股票代码')
plt.ylabel('收益率 (%)')
plt.title('策略收益率对比折线图')
plt.axhline(0, color='gray', linestyle='--', linewidth=0.8)
plt.legend()
plt.grid(True)
plt.tight_layout()

plt.show()

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import matplotlib.pyplot as plt

# 1. 读取数据
df = pd.read_csv('five_stocks_kline_2023.csv', parse_dates=['trade_date'])
df.sort_values(['ts_code', 'trade_date'], inplace=True)

# 2. 直接用已知回测结果构建散点图数据
results = [
    {'ts_code': '000858.SZ', 'MA_Crossover': -21.65, 'RF_Prediction': 3.98},
    {'ts_code': '002594.SZ', 'MA_Crossover': -23.05, 'RF_Prediction': -3.18},
    {'ts_code': '300750.SZ', 'MA_Crossover': -50.94, 'RF_Prediction': -8.71},
    {'ts_code': '600519.SH', 'MA_Crossover': -8.50,  'RF_Prediction': 2.49},
    {'ts_code': '601318.SH', 'MA_Crossover': -15.54, 'RF_Prediction': -3.20},
]
df_res = pd.DataFrame(results)

# 图1：散点图 —— 策略收益对比
plt.figure(figsize=(8, 5))
plt.scatter(df_res['MA_Crossover'], df_res['RF_Prediction'])
for i, code in enumerate(df_res['ts_code']):
    plt.text(df_res['MA_Crossover'][i], df_res['RF_Prediction'][i], code)
plt.xlabel('MA Crossover Return (%)')
plt.ylabel('RF Prediction Return (%)')
plt.title('Scatter of Strategy A vs Strategy B Returns')
plt.show()

# 3. 定义函数，返回净值序列
def ma_series(df_stock, initial=100000):
    df = df_stock.copy()
    df['MA5']  = df['close'].rolling(5).mean()
    df['MA20'] = df['close'].rolling(20).mean()
    df.dropna(inplace=True)
    df['sig'] = 0
    df.loc[df['MA5']>df['MA20'], 'sig'] = 1
    df.loc[df['MA5']<df['MA20'], 'sig'] = -1
    cash, hold, bp = initial, 0, 0
    values = []
    for _, r in df.iterrows():
        p, s = r['close'], r['sig']
        if hold>0:
            ret = (p-bp)/bp
            if s==-1 or ret>=0.10 or ret<=-0.05:
                cash += hold*p; hold=0; bp=0
        if s==1 and hold==0:
            hold = cash//p; cash-=hold*p; bp=p
        values.append(cash+hold*p)
    return df['trade_date'], pd.Series(values, index=df['trade_date'])

def ml_series(df_stock, initial=100000):
    df = df_stock.copy()
    df['ret1'] = df['close'].pct_change(1)
    df['ret2'] = df['close'].pct_change(2)
    df['vol_ma5'] = df['vol'].rolling(5).mean()
    df['vol_r'] = df['vol']/df['vol_ma5']
    df['MA5']  = df['close'].rolling(5).mean()
    df['MA20'] = df['close'].rolling(20).mean()
    df['ma_diff'] = (df['MA5']-df['MA20'])/df['MA20']
    df['next'] = df['close'].shift(-1)
    df['y'] = (df['next']>df['close']).astype(int)
    df.dropna(inplace=True)
    X = df[['ret1','ret2','vol_r','ma_diff']]
    y = df['y']
    split = int(len(X)*0.8)
    Xtr, Xte = X.iloc[:split], X.iloc[split:]
    ytr = y.iloc[:split]
    dfte = df.iloc[split:].copy()
    model = RandomForestClassifier(n_estimators=100, random_state=42)
    model.fit(Xtr, ytr)
    dfte['pred'] = model.predict(Xte)
    cash, hold, bp = initial, 0, 0
    values = []
    for _, r in dfte.iterrows():
        p, s = r['close'], r['pred']
        if hold>0:
            ret = (p-bp)/bp
            if s==0 or ret>=0.10 or ret<=-0.05:
                cash += hold*p; hold=0; bp=0
        if s==1 and hold==0:
            hold = cash//p; cash-=hold*p; bp=p
        values.append(cash+hold*p)
    return dfte['trade_date'], pd.Series(values, index=dfte['trade_date'])

# 4. 以 600519.SH 为例画累计净值曲线
df600 = df[df['ts_code']=='600519.SH'].copy().sort_values('trade_date')
dates_a, val_a = ma_series(df600)
dates_b, val_b = ml_series(df600)

plt.figure(figsize=(8,5))
plt.plot(dates_a, val_a/val_a.iloc[0], label='MA Crossover')
plt.plot(dates_b, val_b/val_b.iloc[0], label='RF Prediction')
plt.xlabel('Date')
plt.ylabel('Normalized Portfolio Value')
plt.title('Cumulative Returns for 600519.SH')
plt.legend()
plt.show()
